package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.HotArticleService;
import com.heima.common.constants.wemedia.ArticleConstants;
import com.heima.common.exception.CustException;
import com.heima.feigns.AdminFeign;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.ArticleVisitStreamMess;
import com.heima.utils.common.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Transactional
public class HotArticleServiceImpl implements HotArticleService {

    @Autowired
    private ApArticleMapper apArticleMapper;

    @Override
    public void computeHotArticle() {
        //查询前五天的(已上架 未删除) 文章数据
        String date = LocalDateTime.now().minusDays(5)
                .format(DateTimeFormatter.ofPattern("yyy-MM-dd 00:00:00"));
        //调用dao层接口 查询文章数据
        List<ApArticle> apArticleList = apArticleMapper.selectArticleByDate(date);
        //计算热点文章分值
        List<HotArticleVo> hotArticleVoList = computeArticleScore(apArticleList);
        //3 为每一个频道缓存热点较高的30条文章
        cacheTagToRedis(hotArticleVoList);

    }

    /**
     * 更新数据库文章分值
     *
     * @param mess
     */
    @Override
    public void updateApArticle(ArticleVisitStreamMess mess) {
        //查询文章
        ApArticle article = apArticleMapper.selectById(mess.getArticleId());
        //判断是否有此文章
        if (article == null) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }
        //更新文章中阅读量
        if (mess.getView() != 0) {
            int views = (int) (article.getViews() == null ? mess.getView() : mess.getView() + article.getViews());
            //设置到文章中
            article.setViews(views);
        }
        //点赞数
        if (mess.getLike() != 0) {
            int likes = (int) (article.getLikes() == null ? mess.getLike() : mess.getLike() + article.getLikes());
            article.setLikes(likes);
        }
        //更新文章中评论数
        if (mess.getComment() != 0) {
            int comment = (int) (article.getComment() == null ? mess.getComment() : mess.getComment() + article.getComment());
            article.setComment(comment);
        }
        //更新文章中收藏量
        if (mess.getCollect() != 0) {
            int collect = (int) (article.getCollection() == null ? mess.getCollect() : mess.getCollect() + article.getCollection());
            article.setCollection(collect);
        }
        //更新数据库
        apArticleMapper.updateById(article);
        //计算文章分值
        Integer score = computeScore(article);
        //若文章是今天发布 热度*3
        String publishTime = DateUtils.dateToString(article.getPublishTime());
        String nowStr = DateUtils.dateToString(new Date());
        if (publishTime.equals(nowStr)) {
            score = score * 3;
        }
        //更新缓存频道
        updateArticleCache(article, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + article.getChannelId());


    }

    /**
     * 更新缓存文章
     *
     * @param article
     * @param score
     * @param cachekey
     */
    private void updateArticleCache(ApArticle article, Integer score, String cachekey) {
        boolean flag = false;
        //获取redis中所有缓存文章
        String hotArticleListJson = redisTemplate.opsForValue().get(cachekey);
        if (StringUtils.isEmpty(hotArticleListJson)) {
            List<HotArticleVo> hotArticleVoList = JSONArray.parseArray(hotArticleListJson, HotArticleVo.class);
            //若有缓存文章更新分值
            for (HotArticleVo hotArticleVo : hotArticleVoList) {
                //若数据库文章id和radis中缓存文章id一致  则更新缓存文章分值
                if (hotArticleVo.getId().equals(article.getId())) {
                    hotArticleVo.setScore(score);
                    flag = true;
                    break;
                }
            }
            //缓存中没有当前文章
            if (!flag) {
                //创建热点文章vo类对象
                HotArticleVo hotArticleVo = new HotArticleVo();
                //将当前文章copy到新的vo类中
                BeanUtils.copyProperties(article, hotArticleVo);
                //设置分数
                hotArticleVo.setScore(score);
                //添加到缓存文章集合中
                hotArticleVoList.add(hotArticleVo);
            }
            //将热点文章集合  按得分降序排序 取前30条

            hotArticleVoList =  hotArticleVoList.stream()
                    .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                    .limit(30)
                    .collect(Collectors.toList());
            redisTemplate.opsForValue().set(cachekey,JSON.toJSONString(hotArticleVoList));
        }
    }

    @Autowired
    AdminFeign adminFeign;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 频道缓存热点较高的30篇文章
     *
     * @param hotArticleVoList
     */
    private void cacheTagToRedis(List<HotArticleVo> hotArticleVoList) {
        //远程调用feign接口  查询所有频道列表
        ResponseResult<List<AdChannel>> responseResult = adminFeign.selectChannels();
        //判断远程调用接口是否成功
        if (responseResult.getCode() == 0) {
            //获取查询到的频道集合
            List<AdChannel> channelList = responseResult.getData();
            //遍历集合
            for (AdChannel adChannel : channelList) {
                //将频道下的每篇热点文章都存到redis中
                List<HotArticleVo> hotArticleVos = hotArticleVoList.stream()
                        //过滤条件是 频道列表相同
                        .filter(hotArticleVo -> hotArticleVo.getChannelId().equals(adChannel.getId()))
                        //转换成集合
                        .collect(Collectors.toList());
                //缓存热点文章到redis
                sortAndCache(hotArticleVos, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + adChannel.getId());
            }
        }
        // 推荐频道直接缓存排序后的前30条
        sortAndCache(hotArticleVoList, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);

    }

    /**
     * 缓存热点文章到redis 方法实现
     *
     * @param hotArticleVos
     * @param cacheKey
     */
    private void sortAndCache(List<HotArticleVo> hotArticleVos, String cacheKey) {
        //文章按照分数降序排序
        hotArticleVos = hotArticleVos.stream()
                //排序
                .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                //只取前30条
                .limit(30)
                //转化为集合
                .collect(Collectors.toList());
        redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(hotArticleVos));

    }

    /**
     * 计算热点文章分值方法实现
     *
     * @param apArticleList
     * @return
     */
    private List<HotArticleVo> computeArticleScore(List<ApArticle> apArticleList) {
        //定义返回集合
        return apArticleList.stream().map(apArticle -> {
            //创建vo对象
            HotArticleVo hotArticleVo = new HotArticleVo();
            //将集合中的对象复制到vo中
            BeanUtils.copyProperties(apArticle, hotArticleVo);
            //计算文章分值
            Integer score = computeScore(apArticle);
            //将分值设置到vo中
            hotArticleVo.setScore(score);
            return hotArticleVo;
        }).collect(Collectors.toList());
    }

    /**
     * 计算文章分支算法
     *
     * @param apArticle
     * @return
     */
    private Integer computeScore(ApArticle apArticle) {
        //定义默认分数
        int score = 0;
        //计算阅读量
        if (apArticle.getViews() != null) {
            score += apArticle.getViews() * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        //计算点赞数
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        //计算评论数
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        //计算收藏量
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }

        return score;
    }
}
